package com.zx.mes.hyl.security.core.validate.code.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.zx.mes.hyl.security.core.properties.SecurityConstants;
import com.zx.mes.hyl.security.core.properties.SecurityProperties;
import com.zx.mes.hyl.security.core.validate.code.ValidateCode;
import com.zx.mes.hyl.security.core.validate.code.ValidateCodeProcessorManager;
import com.zx.mes.hyl.security.core.validate.code.ValidateCodeType;
import com.zx.mes.hyl.security.core.validate.code.exception.ValidateCodeException;
import com.zx.mes.hyl.security.core.validate.code.image.ImageCode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 验证码过滤器
 *
 * @author huayunlong
 * @date 2018-5-31
 */
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private ValidateCodeProcessorManager validateCodeProcessorManager;


    private Map<String,ValidateCodeType> urlMap=new HashMap<>();

    /**
     * SecurityProperties 不采取自动注入，改为传入
     */
    @Autowired
    private SecurityProperties securityProperties;

    /**
     * session 操作
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    /**
     * ant path match  (ant url匹配器)
     */
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    public ValidateCodeFilter() {
    }


    /**
     * 初使化要拦截的url配置信息(需要使用验证码拦截的)
     *
     * @throws ServletException
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();

//        String[] configUrls = StringUtils.splitByWholeSeparator(securityProperties.getCode().getImage().getUrl(), SecurityConstants.DEFAULT_SECURITY_SEPARATOR);
//        if (configUrls != null) {
//            for (String configUrl: configUrls) {
//                urls.add(configUrl);
//            }
//        }
//
//        /* 被过滤的url里必有登录url **/
//        urls.add(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM);
        urlMap.put(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM,ValidateCodeType.IMAGE);
        addUrlToMap(securityProperties.getCode().getImage().getUrl(),ValidateCodeType.IMAGE);

        urlMap.put(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
        addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
    }

    private void addUrlToMap(String url,ValidateCodeType type){
        if (StringUtils.isNotBlank(url)){
            String[] configUrls = StringUtils.splitByWholeSeparator(securityProperties.getCode().getImage().getUrl(), SecurityConstants.DEFAULT_SECURITY_SEPARATOR);
            if (configUrls != null) {
                for (String configUrl: configUrls) {
                    urlMap.put(url,type);
                }
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

//        /* action 为true时，需要被拦截 **/
//        boolean action = false;
//        for (String url: urls) {
//            if (pathMatcher.match(url, request.getRequestURI())) {
//                action = true;
//                break;
//            }
//        }
//
//        if (action) {
//            try {
//                validate(new ServletWebRequest(request));
//                /* 如果校验正确，那么继续下一个过滤器 **/
//
//            } catch (ValidateCodeException e) {
//                /* 验证码校验失败，到此终止，不再到下一个过滤器 **/
//                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
//                return;
//            }
//        }
//        filterChain.doFilter(request, response);

        ValidateCodeType type=getValidateCodeType(request);
        if (type !=null){
            logger.info("校验请求:[{}]中的验证码,验证码类型:[{}]",request.getRequestURI(),type);
            try {
                validateCodeProcessorManager.findValidateCodeProcessor(type)
                        .validate(new ServletWebRequest(request,response));
                logger.info("验证码校验通过");
            } catch (ValidateCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 获取校验码的类型，如果当前请求不需要校验，则返回null
     * @param request HttpServletRequest
     * @return ValidateCodeType
     * @see ValidateCodeType
     */
    private ValidateCodeType getValidateCodeType(HttpServletRequest request){
        ValidateCodeType type=null;
        if (!StringUtils.equalsIgnoreCase(request.getMethod(), "get")) {
            Set<String> urls=urlMap.keySet();
            for (String url:urls){
                if (pathMatcher.match(url,request.getRequestURI())){
                    type=urlMap.get(url);
                    break;
                }
            }
        }

        return type;
    }

    private void validate(ServletWebRequest request) throws ServletRequestBindingException, JsonProcessingException {

        logger.info("DEFAULT_SESSION_IMAGE_KEY:[{}]", SecurityConstants.DEFAULT_SESSION_IMAGE_KEY);

        /* 从 session中获取 imageCode对象 **/
        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,
                SecurityConstants.DEFAULT_SESSION_IMAGE_KEY);

        logger.info("codeInSession:[{}]", codeInSession);

        /* 前端验证码传过来值 的参数名,由此获取此验证码什 **/
        String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), SecurityConstants.DEFAULT_PARAMETER_NAME_CODE_IMAGE);

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("验证码的值不能为空");
        }

        if (codeInSession == null) {
            throw new ValidateCodeException("验证码不存在");
        }

        if (codeInSession.isExpired()) {
            sessionStrategy.removeAttribute(request, SecurityConstants.DEFAULT_SESSION_IMAGE_KEY);
            throw new ValidateCodeException("验证码已过期");
        }

        if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
            throw new ValidateCodeException("验证码不匹配");
        }


    }
}
